//
//  DataSyncHelper.m
//  uteped_sdk
//
//  Created by mateng on 2024/2/28.
//

#import "DataSyncHelper.h"
#import "LogUtils.h"
#import "UTEPedMethodCallHandler.h"
#import "Const.h"
#import "UTEPedDB.h"
#import "SPHelper.h"
#import "DateUtils.h"

#define TAG @"DataSyncHelper"

typedef enum {
    EFetchNone = 0,
    EFetchSleep,
    EFetchStress,
    EFetchWorkout,
    EFetchFitness
} EFetchType;

@interface DataSyncHelper ()

@property(nonatomic, assign) BOOL isSyncRunning;
@property(nonatomic, strong) NSTimer *syncTimer;
@property(nonatomic, strong) NSString *address;

@property(nonatomic, strong) NSMutableArray *sleepData;
@property(nonatomic, strong) NSMutableArray *stressData;
@property(nonatomic, strong) NSMutableArray *workoutData;
@property(nonatomic, strong) NSMutableArray *workoutGpsData;
@property(nonatomic, strong) NSMutableArray *workoutDetailData;
@property(nonatomic, strong) NSMutableArray *fitnessData;

@property(nonatomic, strong) UTEModeSportRecord *sportRecord;
@property(nonatomic, assign) NSInteger workoutCurrent;

@property(nonatomic, assign) NSInteger fitnessCurrent;
@property(nonatomic, assign) NSInteger fitnessCount;

@property(nonatomic, strong) UTEPedDB *utepedDB;
@property(nonatomic, assign) EFetchType fetchType;

- (void)fetchData;
- (void)fetchSleepData;
- (void)fetchStressData;
- (void)fetchWorkoutData;
- (void)syncWorkoutStep:(NSInteger)startTime endTime:(NSInteger)endTime recordId:(NSInteger)recordId;
- (void)syncWorkoutComplete:(NSInteger)startTime endTime:(NSInteger)endTime;

- (void)fetchFitnessData;
- (void)syncFitnessStep:(NSInteger)startTime endTime:(NSInteger)endTime index:(NSInteger)index;
- (void)syncFitnessComplete:(NSInteger)startTime endTime:(NSInteger)endTime;

- (void)startSyncDataTimer;
- (void)removeTimeoutTimer;
- (void)syncDataTimeout;

- (void)syncDataComplete:(NSString*)value;

@end

@implementation DataSyncHelper

static DataSyncHelper* instance = nil;

+ (DataSyncHelper*) shareInstance
{
    if ( instance == nil ) {
        instance = [[DataSyncHelper alloc] init];
    }
    return instance;
}

- (id)init {
    if (self = [super init]) {
        self.blueMgr = [UTEBluetoothMgr sharedInstance];
        self.deviceMgr = [UTEDeviceMgr sharedInstance];
        self.isSyncRunning = NO;
        self.sleepData = [[NSMutableArray alloc] init];
        self.stressData = [[NSMutableArray alloc] init];
        self.workoutData = [[NSMutableArray alloc] init];
        self.workoutGpsData = [[NSMutableArray alloc] init];
        self.workoutDetailData = [[NSMutableArray alloc] init];
        self.fitnessData = [[NSMutableArray alloc] init];
        self.utepedDB = [UTEPedDB shareInstance];
        self.fetchType = EFetchNone;
    }
    
    return self;
}

#pragma mark - Fetch Data
- (void)fetchDataAsync {
    self.address = [[SPHelper shareInstance] getObject:KEY_ADDRESS defaultValue:@""];
    if (![self.blueMgr isOpenBluetooth]) {
        [LogUtils log:TAG message:@"syncData...bluetooth not enabled"];
        [self syncDataComplete:STATE_DEVICE_DISCONNECTED];
        return;
    }
    if (![self.blueMgr.connnectModel isConnected] || self.address.length <= 0) {
        [LogUtils log:TAG message:@"syncData...device not connected"];
        [self syncDataComplete:STATE_DEVICE_DISCONNECTED];
        return;
    }
    else if (self.isSyncRunning) {
        [LogUtils log:TAG message:@"syncData...is running"];
        [self syncDataComplete:STATE_SYNC_RUNNING];
        return;
    }
    
    [self.sleepData removeAllObjects];
    [self.stressData removeAllObjects];
    [self.workoutData removeAllObjects];
    [self.workoutGpsData removeAllObjects];
    [self.workoutDetailData removeAllObjects];
    [self.fitnessData removeAllObjects];
    self.fetchType = EFetchSleep;
    [self performSelectorInBackground:@selector(fetchData) withObject:nil];
    self.isSyncRunning = YES;
}

- (void)fetchData {
    [LogUtils log:TAG message:@"syncData..."];
    
    switch (self.fetchType) {
        case EFetchSleep:
            [self fetchSleepData];
            break;
        case EFetchStress:
            [self fetchStressData];
            break;
        case EFetchWorkout:
            [self fetchWorkoutData];
            break;
        case EFetchFitness:
            [self fetchFitnessData];
            break;
            
        default:
            break;
    }
}

- (void)fetchSleepData {
    __weak typeof(self)weakSelf = self;
    NSInteger end = [[NSDate date] timeIntervalSince1970];
    NSInteger start = end - 7 * 86400;//默认7天数据
    NSDate *st = [NSDate dateWithTimeIntervalSince1970:start];
    NSInteger startTime = [DateUtils getTimeInSeconds:st truncHMS:YES];
    
    [self.deviceMgr getSciSleepModelWithStartTime:startTime endTime:end block:^(NSArray<UTEModelSciSleepFileData *> *array, CGFloat process, BOOL isSuccess, NSInteger errorCode, NSString *filePath, NSDictionary *uteDict) {
        if (isSuccess) {
            [LogUtils log:TAG message:@"syncSleep 睡眠 同步完毕"];
            NSArray *arr = [uteDict valueForKey:@"kSDKQuerySleepDayByDay"];
            for (NSArray *models in arr) {
                [weakSelf.sleepData addObjectsFromArray:models];
            }
            
            if (weakSelf.sleepData.count > 0) {
                [weakSelf.utepedDB saveSleepData:startTime endTime:end models:weakSelf.sleepData address:weakSelf.address];
            }
            weakSelf.fetchType++;
            [weakSelf fetchData];
        } else {
            if (errorCode == UTEDeviceErrorNil) {
                [LogUtils log:TAG message:@"syncSleep...success"];
            } else {
                [LogUtils log:TAG message:[NSString stringWithFormat:@"睡眠同步有问题啦。。。err=%ld", errorCode]];
                weakSelf.fetchType++;
                [weakSelf fetchData];
            }
        }
    }];
    
}

- (void)fetchStressData {
    __weak typeof(self)weakSelf = self;
    NSInteger endTime = [[NSDate date] timeIntervalSince1970];
    NSInteger time = endTime - 24 * 60 * 60;
    NSDate *st = [NSDate dateWithTimeIntervalSince1970:time];
    NSInteger startTime = [DateUtils getTimeInSeconds:st truncHMS:YES];
    
    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
    NSString *file = [NSString stringWithFormat:@"%@/stressFile.bin",docPath];
    [weakSelf.deviceMgr downloadStressDataFile:startTime endTime:endTime filePath:file block:^(CGFloat process, BOOL isSuccess, NSInteger errorCode, NSString *filePath, NSString *uuid,NSDictionary *uteDict) {
        if (isSuccess) {
            NSMutableArray *modelArr = uteDict[@"data"][@"model"];
            if (modelArr != nil && modelArr.count > 0) {
                [weakSelf.stressData addObjectsFromArray:modelArr];
                
                if (weakSelf.stressData.count > 0) {
                    [weakSelf.utepedDB saveStressData:startTime endTime:endTime models:weakSelf.stressData address:weakSelf.address];
                }
            }
            
            weakSelf.fetchType++;
            [weakSelf fetchData];
        } else {
            if (errorCode == UTEDeviceErrorNil) {
                [LogUtils log:TAG message:@"syncStress...success"];
            } else {
                [LogUtils log:TAG message:[NSString stringWithFormat:@"压力同步有问题啦。。。err=%ld", errorCode]];
                weakSelf.fetchType++;
                [weakSelf fetchData];
            }
        }
    }];
}

- (void)fetchWorkoutData:(NSInteger)startTime endTime:(NSInteger)endTime {
    __weak typeof(self)weakSelf = self;
    self.workoutCurrent = 0;
    
    [weakSelf.deviceMgr getRecordList:startTime endTime:endTime block:^(UTEModeSportRecord *model, NSInteger errorCode,NSDictionary *uteDict) {
        if (errorCode == UTEDeviceErrorNil) {
            weakSelf.sportRecord = model;
            if (model != nil && model.recordItemList.count > 0) {
                [weakSelf syncWorkoutStep:startTime endTime:endTime recordId:[model.recordItemList objectAtIndex:0].ID];
            }
            else {
                [LogUtils log:TAG message:@"syncWorkoutData 同步数据为空"];
                [weakSelf syncWorkoutComplete:startTime endTime:endTime];
            }
        } else {
            [LogUtils log:TAG message:@"syncWorkoutData 同步失败"];
            [weakSelf syncWorkoutComplete:startTime endTime:endTime];
        }
    }];
}

- (void)fetchWorkoutData {
    self.workoutCurrent = 0;
    __weak typeof(self)weakSelf = self;
    UInt32 endTime = [[NSDate date] timeIntervalSince1970];
    UInt32 time = endTime - 24 * 60 * 60;
    NSDate *st = [NSDate dateWithTimeIntervalSince1970:time];
    NSInteger startTime = [DateUtils getTimeInSeconds:st truncHMS:YES];
    
    [weakSelf fetchWorkoutData:startTime endTime:endTime];
}

- (void)getWorkoutGps:(NSInteger)recordId {
    __weak typeof(self)weakSelf = self;
    
    [weakSelf.deviceMgr downloadRecordGpsFileModel:recordId filePath:nil block:^(CGFloat process, BOOL isSuccess, NSInteger errorCode, NSArray<UTEModeGpsParam *> *arrayModels) {
        if (isSuccess && errorCode == UTEDeviceErrorNil && arrayModels.count > 0) {
            NSLog(@"record id: %ld - gps data count: %lu", (long)recordId, (unsigned long)arrayModels.count);
            
            NSMutableArray *arr = [NSMutableArray arrayWithCapacity: arrayModels.count];
            
            for(int i = 0; i < arrayModels.count; i++) {
                UTEModeGpsParam *model = [arrayModels objectAtIndex:i];
                
                NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                                      @(model.speed), @"speed",
                                      @(model.distance), @"distance",
                                      @(model.altitude), @"altitude",
                                      @(model.totalDistance), @"totalDistance",
                                      @(model.startTime), @"startTime",
                                      @(model.endTime), @"endTime",
                                      @(model.gpsLatitude), @"gpsLatitude",
                                      @(model.gpsLongitude), @"gpsLongitude",
                                      @(model.gcj02Latitude), @"gcj02Latitude",
                                      @(model.gcj02Longitude), @"gcj02Longitude",
                                      @(model.accuracy), @"accuracy",
                                      @(model.bearing), @"bearing", nil];
                
                [arr addObject:dict];
            }
            NSMutableArray *parentArr = [NSMutableArray array];
            [parentArr addObject:arr];
            
            NSError *error = nil;
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parentArr options:0 error: &error];
            if ([jsonData length] && error == nil) {
                NSString *gpsStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                NSString *startTime = [DateUtils timeStampToString: [[arrayModels firstObject] startTime] fmt:FMT_TIME_SHORT_YMSHMS];
                [weakSelf.utepedDB updateWorkoutGpsData:gpsStr startTime:startTime address:weakSelf.address];
            }
        }
    }];
}

- (void)updateWorkoutDetail {
    __weak typeof(self)weakSelf = self;
    
    for(int i = 0; i < [weakSelf.workoutDetailData count]; i++) {
        UTEModeSportRecordDetail *model = [weakSelf.workoutDetailData objectAtIndex:i];
        NSMutableArray<UTEModeSportRecordDetailList *> *detailDataList = model.detailDataList;
        NSNumber *st = [NSNumber numberWithInteger: model.startTime];
        NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[detailDataList count]];
        if ([detailDataList count]) {
            NSInteger time = model.startTime;
            for(int i = 0; i < [detailDataList count]; i++) {
                UTEModeSportRecordDetailList *detail = [detailDataList objectAtIndex:i];
                
                time = time + model.interval;
                NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [DateUtils timeStampToString: time fmt:FMT_TIME_SHORT_YMSHMS], @"time",
                                      @(detail.speed), @"speed",
                                      @(detail.calorie), @"calorie",
                                      @(detail.elevation), @"elevation",
                                      @(detail.trashFrequency), @"trashFrequency",
                                      @(detail.stepFrequency), @"stepFrequency",
                                      @(detail.heartRate), @"heartRate",
                                      @(detail.swimEfficiency), @"swimEfficiency",
                                      @(detail.jumpSpeed), @"jumpSpeed",
                                      @(detail.fatBurnRate), @"fatBurnRate",
                                      @(detail.sugarBurnRate), @"sugarBurnRate",
                                      @(detail.pace), @"pace", nil];
                [arr addObject:dict];
            }
        }
        
        NSError *error = nil;
        NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[[NSArray alloc] initWithArray:arr] options:0 error: &error];
        if ([jsonData length] && error == nil) {
            NSString *detailStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
            NSString *startTime = [DateUtils timeStampToString: [st integerValue] fmt:FMT_TIME_SHORT_YMSHMS];
            [weakSelf.utepedDB updateWorkoutDetailData:detailStr startTime:startTime address:weakSelf.address];
        }
    }
}

- (void)getWorkoutDetail:(NSInteger)recordId {
    __weak typeof(self)weakSelf = self;
    
    UTEModeRecordDetailParam *param = [UTEModeRecordDetailParam new];
    param.ID = recordId;
    
    [weakSelf.deviceMgr getRecordDetailModel:param block:^(UTEModeSportRecordDetail *model, NSInteger errorCode, NSDictionary *uteDict) {
        if (errorCode == UTEDeviceErrorNil && model.detailDataList.count > 0) {
            NSMutableArray<UTEModeSportRecordDetailList *> *detailDataList = model.detailDataList;
            NSNumber *st = [NSNumber numberWithInteger: model.startTime];
            NSMutableArray *arr = [NSMutableArray arrayWithCapacity:[detailDataList count]];
            if ([detailDataList count]) {
                NSInteger time = model.startTime;
                for(int i = 0; i < [detailDataList count]; i++) {
                    UTEModeSportRecordDetailList *detail = [detailDataList objectAtIndex:i];
                    
                    time = time + model.interval;
                    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [DateUtils timeStampToString: time fmt:FMT_TIME_SHORT_YMSHMS], @"time",
                                          @(detail.speed), @"speed",
                                          @(detail.calorie), @"calorie",
                                          @(detail.elevation), @"elevation",
                                          @(detail.trashFrequency), @"trashFrequency",
                                          @(detail.stepFrequency), @"stepFrequency",
                                          @(detail.heartRate), @"heartRate",
                                          @(detail.swimEfficiency), @"swimEfficiency",
                                          @(detail.jumpSpeed), @"jumpSpeed",
                                          @(detail.fatBurnRate), @"fatBurnRate",
                                          @(detail.sugarBurnRate), @"sugarBurnRate",
                                          @(detail.pace), @"pace", nil];
                    [arr addObject:dict];
                }
            }
            
            NSError *error = nil;
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:[[NSArray alloc] initWithArray:arr] options:0 error: &error];
            if ([jsonData length] && error == nil) {
                NSString *detailStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
                NSString *startTime = [DateUtils timeStampToString: [st integerValue] fmt:FMT_TIME_SHORT_YMSHMS];
                [weakSelf.utepedDB updateWorkoutDetailData:detailStr startTime:startTime address:weakSelf.address];
            }
        }
    }];
}

- (void)syncWorkoutStep:(NSInteger)startTime endTime:(NSInteger)endTime recordId:(NSInteger)recordId
{
    __weak typeof(self)weakSelf = self;
    [weakSelf.deviceMgr getRecordSummary:recordId block:^(UTEModeSportRecordSummary *model, NSInteger errorCode,NSDictionary *uteDict) {
        if (errorCode == UTEDeviceErrorNil) {
            [weakSelf.workoutData addObject:model];
            
            [weakSelf getWorkoutDetail:recordId];
            [weakSelf getWorkoutGps:recordId];
        }
        
        weakSelf.workoutCurrent++;
        if (weakSelf.workoutCurrent >= weakSelf.sportRecord.recordItemList.count) {
            [LogUtils log:TAG message:@"syncWorkoutStep 同步完毕"];
            [weakSelf syncWorkoutComplete:startTime endTime:endTime];
        } else {
            [LogUtils log:TAG message:@"syncWorkoutStep 继续同步"];
            [weakSelf syncWorkoutStep:startTime endTime:endTime recordId:[weakSelf.sportRecord.recordItemList objectAtIndex:weakSelf.workoutCurrent].ID];
        }
    }];
    
}

- (void)syncWorkoutComplete:(NSInteger)startTime endTime:(NSInteger)endTime {
    __weak typeof(self)weakSelf = self;
    if (weakSelf.workoutData.count > 0) {
        [weakSelf.utepedDB saveWorkoutData:startTime endTime:endTime models:weakSelf.workoutData address:weakSelf.address];
    }
    weakSelf.fetchType++;
    [weakSelf fetchData];
}

- (void)fetchFitnessData:(NSInteger)startTime endTime:(NSInteger)endTime{
    __weak typeof(self)weakSelf = self;
    self.fitnessCurrent = 0;
    
    [weakSelf.deviceMgr getSampleFrameList:startTime endTime:endTime block:^(NSInteger count, NSInteger errorCode,NSDictionary *uteDict) {
        if (errorCode == UTEDeviceErrorNil){
            weakSelf.fitnessCount = count;
        }
        
        if (weakSelf.fitnessCount > 0) {
            [weakSelf syncFitnessStep:startTime endTime:endTime index:0];
        }
        else {
            [weakSelf syncFitnessComplete:startTime endTime:endTime];
        }
    }];
}

- (void)fetchFitnessData {
    self.fitnessCurrent = 0;
    __weak typeof(self)weakSelf = self;
    UInt32 endTime = [[NSDate date] timeIntervalSince1970];
    UInt32 time = endTime - 24 * 60 * 60;
    NSDate *st = [NSDate dateWithTimeIntervalSince1970:time];
    NSInteger startTime = [DateUtils getTimeInSeconds:st truncHMS:YES];
    
    [weakSelf fetchFitnessData:startTime endTime:endTime];
}

- (void)syncFitnessStep:(NSInteger)startTime endTime:(NSInteger)endTime index:(NSInteger)index
{
    __weak typeof(self)weakSelf = self;
    [self.deviceMgr getSampleDetailData:startTime
                                               endTime:endTime
                                                 index:index
                                                 block:^(UTEModelMotionFrame *model, NSInteger errorCode, NSDictionary *uteDict) {
        if (errorCode == UTEDeviceErrorNil) {
            [weakSelf.fitnessData addObject:model];
        }
        
        weakSelf.fitnessCurrent ++;
        if (weakSelf.fitnessCurrent >= weakSelf.fitnessCount) {
            [LogUtils log:TAG message:@"syncFitnessStep 同步完毕"];
            [weakSelf syncFitnessComplete:startTime endTime:endTime];
        } else {
            [LogUtils log:TAG message:@"syncFitnessStep 继续同步"];
            [weakSelf syncFitnessStep:startTime endTime:endTime index:weakSelf.fitnessCurrent];
        }
        
    }];
}

- (void)syncFitnessComplete:(NSInteger)startTime endTime:(NSInteger)endTime {
    if (self.fitnessData.count > 0) {
        [self.utepedDB saveFitnessData:startTime endTime:endTime frameItems:self.fitnessData address:self.address];
    }
    [self syncDataComplete:@""];
}

- (void)syncDataComplete:(NSString *)value {
    self.fetchType = EFetchNone;
    self.isSyncRunning = NO;
    dispatch_async(dispatch_get_main_queue(), ^{
        [[UTEPedMethodCallHandler shareInstance] publishLocal:ACTION_SYNC_DATA_COMPLETE value:value];
    });
}

#pragma mark - Timer Sync Data

- (void)startSyncDataTimer
{
    [self removeTimeoutTimer];
    self.syncTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(syncDataTimeout) userInfo:nil repeats:NO];
}

- (void)removeTimeoutTimer
{
    if (self.syncTimer) {
        [self.syncTimer invalidate];
    }
    self.syncTimer = nil;
}

- (void)syncDataTimeout {
    [self removeTimeoutTimer];
    if (self.isSyncRunning) {
        [LogUtils log:TAG message:@"syncData timeout!"];
        [self syncDataComplete:STATE_TIMEOUT];
    }
}

@end
